package com.infoauto.opc.gateway.opc.factory;

import com.infoauto.opc.gateway.messages.binder.RabbitMQBinder;
import com.infoauto.opc.gateway.messages.enums.ExchangeType;
import com.infoauto.opc.gateway.messages.factory.RabbitMQFactory;
import com.infoauto.opc.gateway.messages.model.RabbitMQConsumer;
import com.infoauto.opc.gateway.model.UaServer;
import com.infoauto.opc.gateway.model.UaServerNode;
import com.infoauto.opc.gateway.opc.client.ClientRunner;
import com.infoauto.opc.gateway.opc.client.OpcConnection;
import com.infoauto.opc.gateway.opc.listener.OpcCommandListener;
import com.infoauto.opc.gateway.opc.model.INodeType;
import com.infoauto.opc.gateway.opc.model.SubscribeNode;
import com.infoauto.opc.gateway.opc.utils.SpringContextUtils;
import com.infoauto.opc.gateway.service.UaServerNodesService;
import com.infoauto.opc.gateway.service.UaServerService;
import org.eclipse.milo.opcua.stack.core.security.SecurityPolicy;
import org.eclipse.milo.opcua.stack.core.types.builtin.Variant;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.rabbit.connection.CachingConnectionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;

import java.util.*;

/**
 * OPC UA 连接工厂
 * */
public class OpcClientFactory {
    @Autowired
    private SpringContextUtils springContextUtils;

    /// <summary>
    /// Manager实例
    /// </summary>
    private static OpcClientFactory _manager;
    /// <summary>
    /// 锁定实例
    /// </summary>
    private static Object oClassLock = new Object();

    /// <summary>
    /// 得到该实例
    /// </summary>
    /// <returns></returns>
    public static OpcClientFactory Instance()
    {
        synchronized (oClassLock) //加锁只生成一个实例
        {
            if (_manager == null)
            {
                _manager = new OpcClientFactory();
            }
        }
        return _manager;
    }
    /// <summary>
    /// 私有的构造函数,防止从外部实例化
    /// </summary>
    private OpcClientFactory()
    {
        this.mapConnections = Collections.synchronizedMap(new HashMap<>());
        Environment env = (Environment) springContextUtils.getBean(Environment.class);
        siteCode = env.getProperty("application.sitecode");
    }

    /// <summary>
    /// 得到该实例
    /// </summary>
    public static OpcClientFactory This()
    {
        return Instance();
    }

    private final Logger logger = LoggerFactory.getLogger(getClass());

    private String siteCode;

    private Map<Integer, OpcConnection> mapConnections; //opc连接信息集合
    /**
     * 使用MQTT，接收后端控制指令
     * */
    private void startMQTTConsumer(){
        try {
            CachingConnectionFactory cachingConnectionFactory = (CachingConnectionFactory) springContextUtils.getBean(CachingConnectionFactory.class);
            RabbitMQBinder rmb = new RabbitMQBinder(cachingConnectionFactory);

            rmb.bind("amq.topic","andon.comnand","andon_command", ExchangeType.TOPIC);
            RabbitMQConsumer consumer = RabbitMQFactory.createConsumer(cachingConnectionFactory, "andon_command", new OpcCommandListener());
            consumer.start();
        }catch (Exception e)
        {
            logger.error("receive message error = {}",e.getMessage());
        }
    }

    /**
     * 启动服务
     * */
    public void startServices(Variant variant) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    loadOpcServers();//加載OPC服務信息
                    loadOpcNodes();//加載OPC節點信息
                    WebInterfaceNodeFactory.This().loadThirdInterfaceGroups();//加载第三方接口信息
                    WebInterfaceNodeFactory.This().loadThirdInterfaceNodes();//加载第三方接口属性集合
                    MqttInterfaceNodeFactory.This().loadMqttInterfaceGroups();//加载OPC ANDON数据配置的接口
                    MqttInterfaceNodeFactory.This().loadMqttInterfaceNodes();//加载MQTT_OPC ANDON数据配置的接口属性集合
                   //DeviceLogFactory.This().loadDeviceLogNode();//加载日志节点
                    for(OpcConnection con : mapConnections.values()){
                        if(!con.getIsRunning()){
                            doConnectOpc(con,variant);
                        }
                    }
                    startMQTTConsumer();
                    keepAlive(variant);
                } catch (Exception e) {
                    logger.error("OpcStartListener ERROR:{}",e.getMessage());
                }
            }
        }).start();
    }

    /**
     * 启动连接OPC的线程
     * */
    private void doConnectOpc(OpcConnection con, Variant variant){
        new Thread(new Runnable() {
            @Override
            public void run() {
                    try {
                        new ClientRunner(con, false).run(variant);
                    } catch (Exception e){
                        logger.error("OPC Server = {} has disconnect. 30 seconds later try to reconnect!!!", con.getConnectionID());
                    } finally {
                        //con.setRunning(false);
                        //logger.error("OPC Server = {} has disconnect!!!");
                        //SubscribeNodeFactory.This().resetSubscribeNodeStatus(con.getConnectionID());
                    }
                }
            }
        ).start();
    }



    /**
     * 加载OPC连接
     * **/
    public void loadOpcServers(){
       UaServerService uss = springContextUtils.getBean(UaServerService.class);
        List<UaServer> servers = uss.select(siteCode); //查詢OPC信息
        for (UaServer us : servers){
            logger.info("Loading OpcServers ServerID = {} ServerName = {} OpcUrl = {}",
                    us.getServerID(),
                    us.getServerName(),
                    us.getOpcUrl());

            if(!mapConnections.containsKey(us.getServerID())){
                OpcConnection con = new OpcConnection();
                con.setEndpointUrl(us.getOpcUrl());//赋值OPCURL
                //判断OPC连接校验的安全类型
                if(us.getEncryptionType()==0){
                    con.setSecurityPolicy(SecurityPolicy.None);
                }else if(us.getEncryptionType()==1){
                    con.setSecurityPolicy(SecurityPolicy.Basic128Rsa15);
                }else if(us.getEncryptionType()==2){
                    con.setSecurityPolicy(SecurityPolicy.Basic256);
                }
               //重寫安全驗證
                con.setConnectionID(String.valueOf(us.getServerID()));
                con.setNamespaceIndex(us.getNamespaceIndex());
                con.setRunning(false);
                if(us.getUserName()!=null && !us.getUserName().equals("")){
                    con.setUserName(us.getUserName());
                    con.setPassword(us.getUserPass());
                }else{
                    con.setUserName("");
                    con.setPassword("");
                }
                mapConnections.put(us.getServerID(),con);//叫opc的连接信息放进opc连接信息集合
            }
        }
    }

    private boolean restarting = false;
    /**
     * 清空节点
     * **/
    public void restart(Variant variant){
        restarting = true;

        //删除所有绑定关系
        if(this.mapConnections!=null){
            for (OpcConnection con : this.mapConnections.values()) {
                con.close();
            }
            this.mapConnections.clear();
        }

        WebInterfaceNodeFactory.This().clearNodes();
        MqttInterfaceNodeFactory.This().clearNodes();
        SubscribeNodeFactory.This().clearNodes();
        //DeviceLogFactory.This().clearNodes();

        //重新启动
        this.loadOpcServers();
        this.loadOpcNodes();
        WebInterfaceNodeFactory.This().loadThirdInterfaceGroups();
        WebInterfaceNodeFactory.This().loadThirdInterfaceNodes();
        MqttInterfaceNodeFactory.This().loadMqttInterfaceGroups();
        MqttInterfaceNodeFactory.This().loadMqttInterfaceNodes();
        //DeviceLogFactory.This().loadDeviceLogNode();

        for(OpcConnection con : mapConnections.values()){
            if(!con.getIsRunning()){
                doConnectOpc(con,variant);
            }
        }
        restarting = false;
    }

    /**
     * 加载opc节点
     * */
    public void loadOpcNodes(){
        UaServerNodesService usns = springContextUtils.getBean(UaServerNodesService.class);
        List<UaServerNode> nodes = usns.select(siteCode);//查询所有该OPC下的所有节点
        for (UaServerNode usn : nodes){
            SubscribeNode node = new SubscribeNode();//订阅的节点
            node.setReadType(usn.getReadType());//节点读取方式，0表示订阅，1轮循
            node.setServerID(String.valueOf(usn.getServerID()));//OPC的ID
            node.setNodeID(usn.getNodeID());//opc节点编号
            node.setNodePath(usn.getNodePath());//Opc节点地址
            node.setSubscribe(false);//订阅状态，代表是否订阅，true是已经订阅，false是未订阅
            node.setGroupName(usn.getGroupName());
            node.setNodeName(usn.getNodeName());
            SubscribeNodeFactory.This().addSubscriptNode(node, INodeType.valueOf(usn.getReadType()));//添加到订阅节点集合中
        }
    }

    /**
     * 每10秒钟检查OPC连接是否正常，不正常则重连
     * **/
    private void keepAlive(Variant variant){
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    //TODO 第一次启动的时候，一分钟后再启动检查存活程序
                Thread.sleep((long)1000*60);
                } catch (InterruptedException e) {
                    logger.error("Error :", e.getMessage(), e);
                    Thread.currentThread().interrupt();
                }
                while (true){
                    try {
                        if(!restarting) {
                            for (OpcConnection con : mapConnections.values()) {
                                if(con.getHeartBeat() > 3) {
                                    logger.info("restarting OpcServer....");
                                    restart(variant);
                                    break;
                                } else {
                                    con.setHeartBeat((con.getHeartBeat()+1));
                                }
                            }
                        }
                        Thread.sleep((long)1000*10);
                    } catch (InterruptedException e) {
                        logger.error("Error :", e.getMessage(), e);
                        Thread.currentThread().interrupt();
                    }
                



            }}).start();
    }
    /**
     *  获取心跳连接集合
     */
    public List<Map<String,Object>>   getHeartBeat(){
        Collection<OpcConnection> opcConnections= mapConnections.values();
        List<Map<String,Object>> list = new ArrayList<>();
        for (OpcConnection con : opcConnections) {
            Map<String,Object> map = new HashMap<>();
            map.put("opcUrl",con.getEndpointUrl());
            if(con.getHeartBeat()==0){
                map.put("result","心跳连接正常");
            }else{
                map.put("result","心跳连接异常");
            }
            list.add(map);
        }
         return list;
    }

    /**
     * 工厂代码
     * */
    public String getSiteCode() {
        return siteCode;
    }
}
